﻿#region

using System;
using System.Net.Sockets;
using Jiubang.Communication.Common;

#endregion

namespace Jiubang.Communication.Server{
    public sealed class BufferManager{
        //private readonly AlignedBuffer alignedBuffer;
        private static readonly BufferManager instance = new BufferManager();
        internal IndentedBuffer indentedBuffer;

        internal BufferManager() {
            
        }

        /// <summary>
        ///   是否初始化
        /// </summary>
        public bool IsEnable { get; private set; }

        public static BufferManager Instance{
            get { return instance; }
        }

        /// <param name = "minIndentedLength">锯齿缓冲区最小分组预置长度</param>
        /// <param name = "sections">锯齿缓冲区组数量</param>
        /// <param name = "padding">锯齿缓冲区的最大补白长度</param>
        /// <param name = "indentedSize">缓冲区总容量</param>
        public void Init(int minIndentedLength, int sections, int padding, int indentedSize){
            indentedBuffer = new IndentedBuffer(minIndentedLength, sections, padding, indentedSize);
            IsEnable = true;
        }

        /// <summary>
        ///   回收AsyncEventArgs实例当前缓冲区。<br />
        ///   重新分配指定长度的缓冲空间。
        /// </summary>
        /// <param name = "args">AsyncEventArgs实例</param>
        /// <param name = "messageLength">完整消息包长度</param>
        internal void SetBuffer(SocketAsyncEventArgs args, int messageLength){
            if (messageLength <= 0)
                throw new ArgumentOutOfRangeException("messageLength");
            args.SetBuffer(indentedBuffer.CheckOut(messageLength), 0, messageLength);
        }

        /// <summary>
        ///   分配一段指定长度的缓冲空间
        /// </summary>
        /// <param name = "messageLength"></param>
        /// <returns></returns>
        internal BufferSegment GetBuffer(int messageLength){
            return GetBuffer(messageLength, 0);
        }

        internal BufferSegment GetBuffer(int messageLength, int offset){
            return new BufferSegment(indentedBuffer.CheckOut(messageLength), offset);
        }

        /// <summary>
        ///   判断现有缓冲区大小是否达到期望值，未达到则重新分配一个足够大的缓冲空间，并将数据拷贝入新缓冲区中。
        /// </summary>
        /// <param name = "args"></param>
        /// <param name = "expectedMessageLength">完整消息包长度</param>
        internal void ResetBufferSize(SocketAsyncEventArgs args, int expectedMessageLength){
            if (expectedMessageLength <= args.BytesTransferred){
                throw new ArgumentOutOfRangeException("expectedMessageLength",
                                                      "该值为[" + expectedMessageLength + "]必须大于已传输的字节数[" +
                                                      args.BytesTransferred + "],Socket[" +
                                                      CommonServer.GetSocketDescrption(args.AcceptSocket) + "]");
            }

            ResetBufferSize(expectedMessageLength, args.Buffer.Length, args, indentedBuffer.CheckIn, args.Buffer);
        }

        private void ResetBufferSize<T>(int appliedBufferLength, int currentBufferLength, SocketAsyncEventArgs args,
                                        Action<T, bool> checkIn, T checkInObject){
            var userToken = (AsyncUserToken) args.UserToken;
            var headerOffset = userToken.MessageHeaderOffset;
            var readBytesCount = args.BytesTransferred + (args.Offset - headerOffset);
            //判断缓冲区长度足够
            if (currentBufferLength >= appliedBufferLength){
                args.SetBuffer(args.Offset + args.BytesTransferred, appliedBufferLength - readBytesCount);
            } else{
                //缓冲区长度不足从锯齿缓冲区申请
                var buffer = indentedBuffer.CheckOut(appliedBufferLength);

                //复制已读消息至新缓冲区
                Buffer.BlockCopy(args.Buffer, headerOffset, buffer, 0, readBytesCount);

                //回收缓冲区空间
                checkIn(checkInObject, false);

                //设置新缓冲区
                args.SetBuffer(buffer, readBytesCount, appliedBufferLength - readBytesCount);
                userToken.MessageHeaderOffset = 0;
            }
        }

        /// <summary>
        ///   释放AsyncEventArgs参数的缓冲区。<br />
        ///   如缓冲区属于预置缓冲区登记为空闲。
        /// </summary>
        /// <param name = "args"></param>
        /// <param name = "onError">是否异常状态回收。在该值为真时，属于indentedBuffer缓冲将不重新加入池</param>
        internal void FreeBuffer(SocketAsyncEventArgs args, bool onError){
            if (args.Buffer == null) return;
            FreeBuffer(args.Buffer, onError);
            args.SetBuffer(null, 0, 0);
        }

        internal void FreeBuffer(byte[] buffer, bool onError){
            indentedBuffer.CheckIn(buffer, onError);
        }

        public void FreeBuffer(MessageContext message){
            if (message.Segment.array != null)
                FreeBuffer(message.Segment.array, false);
        }
    }
}